<template>
  <div id="hy-table" class="rounded bg-white">
    <slot name="title" :row="additionalData" />
    <el-alert
      v-if="isSaveToUpadate"
      title="该页面可同时批量新增、编辑。填写好数据后点击保存按钮，即可保存当前更改或者新增的数据"
      type="info"
      close-text="知道了"
    >
    </el-alert>
    <slot name="tableTitle"> </slot>

    <el-form>
      <el-table
        ref="tableRef"
        style="width: 100%"
        v-bind="childrenProps"
        highlight-current-row
        :height="height"
        :data="listData"
        :cell-style="() => cellStyle"
        :header-cell-style="() => headerCellStyle"
        @selection-change="handleSelectionChange"
        @row-dblclick="rowsDbclick"
        @row-click="singleClick"
        :row-key="selectKey"
        :tree-props="treeProps"
      >
        <el-table-column
          v-if="selectColumnType === 'RADIO'"
          align="center"
          width="100"
          label="选择"
          fixed
        >
          <template #default="scope">
            <el-radio
              v-if="scope.row[selectKey]"
              class="radio flex"
              v-model="selectedData"
              :label="scope.row[selectKey]"
              @input="handleSelectionChange"
            />
          </template>
        </el-table-column>

        <el-table-column
          v-if="selectColumnType === 'CHECKBOX'"
          type="selection"
          align="center"
          width="60"
          fixed
          :reserve-selection="true"
          :selectable="disableIdsReturen"
        />

        <el-table-column
          v-if="showIndexColumn"
          type="index"
          label="序号"
          align="center"
          width="80"
          fixed
        />

        <el-table-column
          v-bind="propItem"
          v-for="(propItem, index) in propList"
          :label="propItem.label"
          :key="propItem.prop"
          :header-align="propItem.headerAlign"
          :align="propItem.align"
          :width="propItem.width"
          show-overflow-tooltip
        >
          <template v-if="propItem.headerSlotName" #header="column">
            <slot :name="propItem.headerSlotName" v-bind="column"> </slot>
          </template>

          <template #default="scope">
            <slot :name="propItem.slotName" v-bind:row="scope.row">
              <el-form-item
                v-if="checkoutData(scope.row[selectKey]) && propItem.operate"
                size="mini"
                :prop="propItem.prop"
                :style="formItemStyle"
                :ref="
                  propItem.operate
                    ? 'table-' + scope.row[selectKey] + '-el-form-item-' + index
                    : ''
                "
                @keyup.enter.native="nextFocus(scope.row[selectKey], index)"
              >
                <template
                  v-if="
                    ['input', 'password', 'textarea'].includes(
                      propItem.operate.type
                    )
                  "
                >
                  <el-input
                    :placeholder="propItem.operate.placeholder"
                    :type="propItem.operate.type"
                    v-bind="propItem.operate.otherOptions"
                    :show-password="propItem.operate.type === 'password'"
                    v-model="scope.row[propItem.operate.field]"
                  />
                </template>
                <!-- cascader联级选择器 -->
                <template v-else-if="propItem.operate.type === 'cascade'">
                  <el-cascader
                    :placeholder="propItem.operate.placeholder"
                    v-bind="propItem.operate.otherOptions"
                    style="width: 100%"
                    v-model="scope.row[`${propItem.operate.field}`]"
                    :options="propItem.operate.options"
                    :props="{
                      label: propItem.operate.optionsKey,
                      value: propItem.operate.optionsValue,
                      children: propItem.operate.optionsChildren,
                      checkStrictly: true,
                    }"
                    clearable
                  >
                  </el-cascader>
                </template>
                <!-- select选择器 -->
                <template v-else-if="propItem.operate.type === 'select'">
                  <el-select
                    :placeholder="propItem.operate.placeholder"
                    v-bind="propItem.operate.otherOptions"
                    style="width: 100%"
                    v-model="scope.row[`${propItem.operate.field}`]"
                  >
                    <el-option
                      v-for="option in propItem.operate.options"
                      :key="option[propItem.operate.optionsValue || 'value']"
                      :label="option[propItem.operate.optionsKey || 'label']"
                      :value="option[propItem.operate.optionsValue || 'value']"
                    >
                    </el-option>
                  </el-select>
                </template>
                <!-- 日期选择器 -->
                <template
                  v-else-if="
                    propItem.operate.type === 'datePicker' ||
                    propItem.operate.type === 'daterange'
                  "
                >
                  <el-date-picker
                    style="width: 100%"
                    v-bind="propItem.operate.otherOptions"
                    v-model="scope.row[`${propItem.operate.field}`]"
                  >
                  </el-date-picker>
                </template>
                <!-- inputNumber计数器 -->
                <template v-else-if="propItem.operate.type === 'inputNumber'">
                  <el-input-number
                    style="width: 100%"
                    v-bind="propItem.operate.otherOptions"
                    v-model="scope.row[`${propItem.operate.field}`]"
                  />
                </template>
                <!-- 单选多选按钮 -->
                <template v-else-if="propItem.operate.type === 'radio'">
                  <el-radio-group
                    v-model="scope.row[`${propItem.operate.field}`]"
                    :disabled="propItem.operate.otherOptions.disabled"
                  >
                    <el-radio
                      v-for="option in propItem.operate.options"
                      :label="option[propItem.operate.optionsValue || 'value']"
                      :key="option"
                    >
                      {{ option[propItem.operate.optionsKey || "label"] }}
                    </el-radio>
                  </el-radio-group>
                </template>
                <!-- switch滑块按钮 -->
                <template v-else-if="propItem.operate.type === 'switch'">
                  <el-switch
                    v-model="scope.row[`${propItem.operate.field}`]"
                    v-bind="propItem.operate.otherOptions"
                  />
                </template>
              </el-form-item>
              <div v-else-if="propItem.echo">
                {{
                  echoField(
                    propItem.echo.options,
                    propItem.echo.optionsValue,
                    propItem.echo.optionsKey,
                    scope.row[propItem.prop]
                  )
                }}
              </div>
              <div v-else>{{ returnProps(scope.row[propItem.prop]) }}</div>
            </slot>
          </template>
        </el-table-column>

        <el-table-column
          v-if="operateWether"
          width="'50px'"
          label="操作"
          align="center"
          fixed="right"
        >
          <template v-slot="{ row }">
            <slot name="operate" :row="row" />
          </template>
        </el-table-column>
      </el-table>
    </el-form>
    <div class="footer flex justify-center pb-8 pt-8" v-if="showFooter">
      <slot name="footer">
        <el-pagination
          :hide-on-single-page="listData.length <= 11"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="searchForm.pageNum"
          :page-size="searchForm.pageSize"
          :page-sizes="[10, 20, 30]"
          :layout="paginationLayout"
          :total="listCount"
          backgound
        />
      </slot>
    </div>
  </div>
</template>

<script>
import { operateTableDataBody } from "@/api/table";
import { mapActions } from "vuex";

export default {
  name: "l-table",
  data() {
    return {
      searchForm: {
        pageSize: 10,
        pageNum: 1,
      }, // 查询条件 - 页码条数
      listData: [], // 表格数据集合
      additionalData: {}, // 额外数据
      listCount: 0, // 总条数
      selectedData: [], // 选中列表集合
      tableRef: null, // table对象
      editRows: [], // 更改列表
    };
  },
  props: {
    // 高度
    height: {
      type: [String, Number],
    },

    // 表格字段
    propList: {
      type: Array,
      required: true,
    },

    // 是否展示序号
    showIndexColumn: {
      type: Boolean,
      default: false,
    },

    // 是否单选多选
    selectColumnType: {
      type: String,
      default: "CHECKBOX",
    },

    // 是否允许操作
    isOperate: {
      default() {
        return false;
      },
    },

    // 是否自定义双击事件
    CustomDoubleClick: {
      default() {
        return false;
      },
    },
    //是否是save接口发请求--lb
    isSaveToUpadate: {
      default() {
        return false;
      },
    },
    // 子级参数
    childrenProps: {
      type: Object,
      default: () => ({}),
    },

    // 展示页脚
    showFooter: {
      type: Boolean,
      default: true,
    },

    // 查找类型 findAll 查询所有 || findPage 查询分页
    findType: {
      type: String,
      default: "findPage",
    },

    // 单元格样式
    cellStyle: {
      default() {
        return {};
      },
    },

    // 表头单元格样式
    headerCellStyle: {
      default() {
        return {};
      },
    },

    // 表格表单子样式
    formItemStyle: {
      default() {
        return {};
      },
    },

    // 禁止选中行集合
    disableIds: {
      type: Array,
      default() {
        return [];
      },
    },

    // 查询接口配置
    reqRequest: {
      type: Object,
      default() {
        return {
          url: "",
          methods: "get",
        };
      },
    },

    // 查询接口传入参数
    propsSearchForm: {
      type: Object,
      default() {
        return {};
      },
    },

    // 是否初始化查询
    initialize: {
      type: Boolean,
      default() {
        return true;
      },
    },

    // 是否查询条件改变查询
    changeSearchForm: {
      type: Boolean,
      default() {
        return true;
      },
    },

    // 更改接口
    setRequest: {
      default() {
        return {};
      },
    },

    // 新增接口
    addRequest: {
      default() {
        return {};
      },
    },
    // save接口 --lb
    saveRequest: {
      default() {
        return {};
      },
    },

    // 表格主键字段
    selectKey: {
      type: String,
      default() {
        return "id";
      },
    },

    // 分页器排版
    paginationLayout: {
      type: String,
      default() {
        return "total, sizes, prev, pager, next, jumper";
      },
    },

    // 是否有操作字段插槽
    operateWether: {
      type: Boolean,
      default() {
        return false;
      },
    },

    // 树状传值
    treeProps: {
      type: Object,
      default() {
        return {};
      },
    },
  },
  mounted() {
    if (this.initialize) this.getListData();
  },
  methods: {
    ...mapActions("custom_dict", ["setDict"]),
    // 查询数据
    async getListData(queryInfo) {
      console.log(11);
      // 搜索条件
      let form_config = { ...this.propsSearchForm };
      const form = { ...this.reqRequest };

      // 判断是否是分页, 反之则是全部
      if (this.findType === "findPage") {
        for (let key in this.searchForm) {
          form_config[key] = this.searchForm[key];
        }
      }
      // 重置表格集合数据和编辑集合数据
      this.listData = [];
      this.editRows = [];

      // 查询所有
      const res = await operateTableDataBody(form, form_config);
      if (res.rows) {
        const { data, ...surplus } = res.rows;
        this.listCount = res.total || res.rows.total;
        this.listData = res.rows || res.data;
        this.additionalData = surplus;
      }

      if (res.data) {
        const { data, ...surplus } = res.data;
        this.listCount =
          res.total ||
          res.data.total ||
          res.data.length ||
          res.data.data.length;
        this.listData = res.data.data || res.data;
        this.additionalData = surplus;
      }

      console.log(this.additionalData);

      if (this.addRequest.url || this.isSaveToUpadate) {
        // 允许新增则添加新增对象
        this.addOneRow();
      }
    },
    //--lb  把getListData 函数的新增行代码提取出来
    addOneRow() {
      // 允许新增则添加新增对象
      if (this.addRequest.url || this.saveRequest.url) {
        // 创建新增对象
        const add_Obj = {};
        this.propList.forEach((item) => {
          if (item.operate) {
            add_Obj[item.operate.field] = "";
          }
        });

        // 回显新增数据对象
        this.listData.push(add_Obj);
        this.editRows.push(add_Obj);
      }
    },
    // 选中方法
    handleSelectionChange(rows) {
      this.selectedData = []; // 只有主键的集合
      let selectedArr = []; // 返还给父级组件的详情数据

      // 区分多选和单选
      if (typeof rows === "number") {
        const data = this.listData.find((row) => {
          return row[this.selectKey] === rows;
        });
        this.selectedData = rows;
        selectedArr.push(data);
      } else {
        if (rows) {
          selectedArr = rows.map((row) => {
            if (row) {
              this.selectedData.push(row[this.selectKey]);
              return row;
            }
          });
        }
      }

      this.$emit("selectionChange", selectedArr);
    },

    // 禁止选中多选框集合方法
    disableIdsReturen(row) {
      if (this.CustomDoubleClick) {
        return true;
      }
      return !(
        !row[this.selectKey] || this.disableIds.includes(row[this.selectKey])
      );
    },

    // 清空选中集合
    clearSelection() {
      this.tableRef.clearSelection();
    },

    // 返回结果为空时候的处理
    returnProps(data) {
      if (data !== null && data !== undefined) {
        return data;
      } else {
        return "--";
      }
    },

    // 分页
    handleCurrentChange(page) {
      this.searchForm.pageNum = page;
      this.getListData();
    },

    // 大小
    handleSizeChange(size) {
      this.searchForm.pageSize = size;
      this.getListData();
    },

    // 双击事件
    rowsDbclick(row) {
      if (this.isOperate) {
        const check = this.checkoutData(row[this.selectKey]); // 检查编辑对象内是否存在
        if (!check) this.editRows.push(row); // 为编辑对象数组中添加单条数据
        console.log(this.editRows, check);
      } else if (this.CustomDoubleClick) {
        // 自定义双击事件 --lb
        this.$emit("CustomDoubleClick", row);
      }
    },

    // 单击事件 --lb
    singleClick() {
      if (this.isSaveToUpadate) {
        this.rowsDbclick()
      }
    },

    // table表格键盘Enter事件 row：每条数据的主键数据
    nextFocus(row, col) {
      const data = this.editRows.find((item) => {
        return item[this.selectKey] === row;
      }); // 找到单条数据

      const check = this.checkoutField(data, col); // 校验当前字段是否符合规则
      // 该条数据的单个字段校验是否通过
      if (check) {
        let dimension = col + 1; // 下一个格数
        // 判断是否是在最后一个
        if (dimension < this.propList.length) {
          const Target =
            this.$refs["table-" + row + "-el-form-item-" + dimension]; // 获取下一个表单项实例对象
          console.log("Target", Target);
          if (Target !== undefined) {
            let Inner_Dom =
              Target[0].$el.querySelector(".el-input__inner") ||
              Target[0].$el.querySelector(".el-select"); // 查找el-input__inner元素
            // 假如当下一个表单项不是.el-input__inner的内容, 则跳过此配置下一个重新执行nextFocus函数

            console.log("Inner_Dom ", Inner_Dom);
            if (Inner_Dom === null) {
              this.nextFocus(row, col + 1);
              return;
            }
            Inner_Dom.focus(); // 获取焦点
          } else {
            this.operate(data);
          }
        } else {
          this.operate(data);
        }
      }
    },

    // 校验单个字段是否符合规则
    checkoutField(data, col) {
      const itemConfig = this.propList[col]; // 找到当前配置项
      const keyValue = data[itemConfig.prop]; // 当前该字段的键值

      let check = true; // 数据的指定字段是否通过校验
      // 判断是否存在校验规则, 且符合规则
      if (itemConfig.rules) {
        check = itemConfig.rules.every((item) => {
          if (item.require) return item.require;
          if (item.reg) return item.reg.test(keyValue);
          if (item.max && item.min) {
            return keyValue < item.max && keyValue > item.min;
          }
          return false;
        });
      }

      return check;
    },

    // 回显定义的字段
    echoField(data, key, labelKey, value) {
      // 在数组中找到该对象
      const temporary = data.find((item) => {
        return item[key] === value;
      });

      return temporary ? temporary[labelKey] : "未找到";
    },

    // 添加自定义数据
    setListData(list) {
      this.listData = list;
    },

    // 判断其是否存在在更改对象重
    checkoutData(Key) {
      // 查找编辑数组内是否存在单条数据相符合
      const check = this.editRows.some(
        (item) => item[this.selectKey] === Key && Key !== "undefiend"
      );
      return check || false;
    },

    // 操作事件 新增/编辑
    async operate(data) {
      const checkArr = this.propList.map((item, index) => {
        return this.checkoutField(data, index);
      });

      // 检查data的需要检查的字段是否符合校验
      const check = checkArr.findIndex((item) => {
        return item === false;
      });
      if (check) {
        const requestApi =
          data[this.selectKey] === undefined
            ? this.addRequest
            : this.setRequest;
        // 编辑/新增
        if (check) {
          //--lb
          if (this.isSaveToUpadate) {
            this.$message.info(
              "点击保存按钮，保存当前更改的数据或者继续编辑、添加"
            );
            return this.addOneRow();
          } else {
            operateTableDataBody(requestApi, data).then(() => {
              this.getListData();
              this.$message.success(
                data[this.selectKey] !== undefined ? "编辑完成" : "新增完成"
              );
              // 判断是否更新自定义字典
              if (requestApi.updateCache) {
                this.setDict(); // 更新字典
              }
            });
          }
        }
      } else {
        this.$message.error("不符合规则, 请重试!");
      }
    },
    // 专为sava接口写的请求--lb
    saveToUpdate() {
      if (!this.editRows.length)
        return this.$message.error("当前没有新数据需要更新");
      this.editRows.forEach((obj, index) => {
        if (this.objIsEmpty(obj)) this.editRows.splice(index, 1);
      });
      operateTableDataBody(this.saveRequest, this.editRows).then((res) => {
        this.getListData();
        this.$message.success(res.msg);
        // 判断是否更新自定义字典
        if (this.saveRequest.updateCache) {
          this.setDict(); // 更新字典
        }
      });
    },
    //校验对象是否为空
    objIsEmpty(obj) {
      let flage = true;
      for (let key in obj) {
        console.log(obj[key], 1);
        flage = flage && !obj[key]; //每一项都为空
      }
      return flage;
    },
  },
};
</script>

<style scoped lang="scss">
.header {
  .handler {
    align-items: center;
  }
}

.footer {
  margin-top: 15px;

  .el-pagination {
    text-align: right;
  }
}

.el-table__row > td {
  border: none;
}

.el-table::before {
  height: 0;
}
</style>






